
module HeroLingo
  class Music
    
    attr_reader :level
    
    def initialize(data, &fret_sequence_block)
      
      validate_data data
      
      @level = data[:level]
      @title = data[:title]
      
      clear_frets
      fret_sequence_block.call
      fret_sequence = Fixnum.current_frets
      validate_fret_sequence fret_sequence
      @frets = fret_sequence
    end
    
    def length
      15.frets
    end
    
    
    private 
    def validate_data data_to_be_validated
      raise 'A title must be defined for a music' unless data_to_be_validated[:title]
      raise 'A difficulty level must be defined for a music' unless data_to_be_validated[:level]
    end
    
    def allowed_buttons
        allowed=[]
        allowed = [:green, :red, :yellow] if level == :easy
        allowed = [:green, :red, :yellow, :blue] if level == :medium
        allowed = [:green, :red, :yellow, :blue, :orange] if level == :hard
        allowed
    end
    
    def extract_button_from (button_pattern)
      button = 'NOT INIIALIZED'
      button = button_pattern if button_pattern.kind_of? Symbol
      button = button_pattern.values.first if button_pattern.kind_of? Hash
      button
    end

    def check_if_color_is_allowed(button)
      raise "Illegal button [#{button}] for music from the [#{level}] level. Please choose between [green, red, yellow]" unless allowed_buttons.include? button
    end
    
    def validate_fret_sequence(sequence)
      open_long_notes=[]
      frets_already_set = []
              
      sequence.each do |fret| 
        
        raise "Fret [#{fret[:number]}] defined more than once" if frets_already_set.include? fret[:number]
        frets_already_set << fret[:number]
        
        buttons_already_set = [] 
        
        fret[:buttons].each do |button_pattern|
          raise "Button [#{button_pattern}] is repeated at fret [#{fret[:number]}]. It is not possible to use the same button more than once at a fret." if buttons_already_set.include? button_pattern
          
          
          button = extract_button_from button_pattern        
          
          check_if_color_is_allowed button

          if is_a_long_note_start button_pattern then
            raise '' if open_long_notes.include? button
            open_long_notes << button
          end

          if is_a_long_note_end button_pattern then
            raise "Trying to end a [#{button}] long note that was not started at fret [#{fret[:number]}]" if not open_long_notes.include? button
            open_long_notes.delete button
          end
          
          buttons_already_set << button_pattern
        end
      end  
      
      raise "Started a [#{open_long_notes.first}] long note and never ended" unless open_long_notes.empty?
      
    end
    
    def clear_frets
      Fixnum.clear_frets
    end
    
    def is_a_long_note_start button_pattern
      result = false
      result = button_pattern.keys.first == :start if button_pattern.kind_of? Hash
      result
    end
    
    def is_a_long_note_end button_pattern
      result = false
      result = button_pattern.keys.first == :end if button_pattern.kind_of? Hash
      result
    end
  end
end